package com.lehecai.core.lottery.fetcher.lotterydraw.impl.worker;

import com.lehecai.core.lottery.LotteryType;
import com.lehecai.core.lottery.fetcher.lotterydraw.LotteryDraw;
import com.lehecai.core.lottery.fetcher.lotterydraw.LotteryDrawPrizeItem;
import com.lehecai.core.util.CharsetConstant;
import com.lehecai.core.util.CoreDateUtils;
import com.lehecai.core.util.CoreStringUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.*;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.tags.TableColumn;
import org.htmlparser.tags.TableRow;
import org.htmlparser.tags.TableTag;
import org.htmlparser.util.NodeList;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.*;

/**
 * 浙江体彩官网抓取
 * @author Sunshow
 *
 */
public class LotteryDrawFetchWorkerZJTC extends AbstractLotteryDrawFetchWorker{

	protected static final String BASE_URL = "http://www.zjlottery.com/";
    protected static final String BASE_URL_XML = "http://www.zjlottery.com/gtxml/index.asp";
	
	private static Map<LotteryType, String> lotteryValueMap = new HashMap<LotteryType, String>();
    private static Map<LotteryType, String> lotteryNameMap = new HashMap<LotteryType, String>();

    private Object lock = new Object();

	static {
		lotteryValueMap.put(LotteryType.A_ZJ20, "6");
		lotteryValueMap.put(LotteryType.A_ZJ6, "1");
        lotteryNameMap.put(LotteryType.A_ZJ20, "20选5");
        lotteryNameMap.put(LotteryType.A_ZJ6, "６+１");
	}

	public LotteryDrawFetchWorkerZJTC(LotteryType lotteryType){
		super(lotteryType);
	}
	
	@Override
	public LotteryDraw fetchResult(String phase) {
		return this.fetchResultDetail(phase);
	}

	@Override
	public LotteryDraw fetchResultDetail(String phase) {
		LotteryType lotteryType = this.getLotteryType();
		String encoding = CharsetConstant.CHARSET_GB2312;
		String toFetchPhase = phase;
		if (StringUtils.isEmpty(toFetchPhase)) {			//未指定彩期，获取最新期
            toFetchPhase = getFetchPhaseFromIndexPage(encoding, lotteryType);
            if (StringUtils.isEmpty(toFetchPhase)) {
                toFetchPhase = getFetchPhaseFromHistoryPage(encoding, lotteryType);
            }
		}
		logger.info("要抓取的彩期号={}, lotteryType={}", toFetchPhase, lotteryType.getName());

		String resultDetailUrl = getResultDetailUrl(toFetchPhase);
		if (StringUtils.isEmpty(resultDetailUrl)) {
			logger.error("无法解析到详情地址");
			return null;
		}
		logger.info("详情地址：{}", resultDetailUrl);
		String data = null;
		try {
            synchronized (lock) {
                data = this.fetch(resultDetailUrl, METHOD.GET, null, null, encoding);
            }
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		if (data == null || data.indexOf("404 Not Found") > 0) {
            logger.error("抓取超时，未抓取列表页面内容信息");
			return null;
		}
		Parser parser;
		try {
			parser = Parser.createParser(data, encoding);
		} catch (Exception e) {
			logger.error("解析html出错, data={}", data);
			return null;
		}
		
		LotteryDraw lotteryDraw = new LotteryDraw();
		lotteryDraw.setLotteryType(lotteryType);
		
		try {
			NodeFilter tableFilter = new TagNameFilter("table");
			NodeList tableNodeList = parser.extractAllNodesThatMatch(tableFilter);
			if (tableNodeList != null && tableNodeList.size() != 0) {
				TableTag tableTag = (TableTag)tableNodeList.elementAt(1);
				TableColumn phaseColumn = tableTag.getRow(0).getColumns()[0];			//第一行 第一列为彩期
				String phaseStr = phaseColumn.getStringText();
				lotteryDraw.setPhase(getPhase(phaseStr));
                // 第一行 第一列为开奖时间
                Parser drawTimeParser;  //开奖时间
                NodeList nodeDrawTimeList;//开奖时间节点
                drawTimeParser = Parser.createParser(phaseColumn.getStringText(), CharsetConstant.CHARSET_GBK);
                nodeDrawTimeList = drawTimeParser.extractAllNodesThatMatch(new StringFilter("开奖日期"));
                String drawTimeStr = "";
                String drawTime;
                if (nodeDrawTimeList != null) {
                    drawTimeStr = nodeDrawTimeList.elementAt(0).getParent().toPlainTextString();
                }
                if (StringUtils.isNotEmpty(drawTimeStr) && drawTimeStr.indexOf("：") > 0) {
                    drawTime = drawTimeStr.substring(drawTimeStr.indexOf("：") + 1);
                } else {
                    logger.error(lotteryValueMap.get(this.getLotteryType()) + "获取不到开奖时间");
                    return null;
                }
                lotteryDraw.setTimeDraw(getTimeDraw(trimAll(drawTime)));
				
				if (!toFetchPhase.equals(lotteryDraw.getPhase())) {
					logger.error("要抓取的数据与指定彩期不匹配, fetchedPhase={}, phase={}, LotteryType={}", lotteryDraw.getPhase(), toFetchPhase, this.getLotteryType().getName());
					return null;
				}
                Parser totalParser;    //销售额
                Parser awardParser;    //开奖号码
                Parser jackpotParser;  //奖池金额
                NodeList nodeTotalList;//总投注额节点
                NodeList nodeAwardList;//开奖号码节点
                NodeList nodeJackpotList;//奖池金额节点
				TableColumn volumeOfSalesColumn = tableTag.getRow(1).getColumns()[0];			//第二行 第一列为销售额
                String volumnOfSalesStr = "";
				String volumnOfSales = volumeOfSalesColumn.getStringText();
                totalParser = Parser.createParser(volumnOfSales, CharsetConstant.CHARSET_GBK);
                awardParser = Parser.createParser(volumnOfSales, CharsetConstant.CHARSET_GBK);
                jackpotParser = Parser.createParser(volumnOfSales, CharsetConstant.CHARSET_GBK);
                nodeTotalList = totalParser.extractAllNodesThatMatch(new StringFilter("总投注额"));
                nodeAwardList = awardParser.extractAllNodesThatMatch(new CssSelectorNodeFilter("div[class='award']"));
                nodeJackpotList = jackpotParser.extractAllNodesThatMatch(new CssSelectorNodeFilter("div[class='float_r']"));
                if (nodeTotalList.size() > 0) {
                    volumnOfSalesStr = nodeTotalList.elementAt(0).getParent().toPlainTextString();
                }
				volumnOfSalesStr = trimAll(volumnOfSalesStr.replace("总投注额", "").replace("</b>", ""));
				lotteryDraw.setVolumeOfSales(getVolumeOfSales(volumnOfSalesStr));
                NodeList nodeList = new NodeList();
                if (nodeAwardList.size() > 0) {
                    nodeList = nodeAwardList.elementAt(0).getChildren();
                }
                String result;
                StringBuilder sb = new StringBuilder("");
                if (nodeList != null && nodeList.size() == 17) {  //6选1
                    sb.append(nodeList.elementAt(3).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(5).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(7).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(9).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(11).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(13).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(15).toPlainTextString());
                }
                if (nodeList != null && nodeList.size() == 13) { //25选5
                    sb.append(nodeList.elementAt(3).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(5).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(7).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(9).toPlainTextString()).append(",");
                    sb.append(nodeList.elementAt(11).toPlainTextString()).append(",");
                }
                result = sb.toString();
                if (StringUtils.isEmpty(result)) {
					logger.error("不正确的开奖号码, result={}", result);
					return null;
				}
				result = CoreStringUtils.replaceAll(result, new String[][] {{" ", ","}, {"+", ""}});
				lotteryDraw.setResult(result);
                String jackpotStr = "";
                if (nodeJackpotList.size() > 0) {
                    jackpotStr = nodeJackpotList.elementAt(0).toPlainTextString();
                }
                jackpotStr =  trimAll(CoreStringUtils.substringBetween(jackpotStr, "总额", "全额"));//处理奖池金额特殊情况
                if (StringUtils.isNotEmpty(jackpotStr) && jackpotStr.endsWith("万元")) {
                    BigDecimal bigDecimal = new BigDecimal(jackpotStr.replace("万元",""));
                    BigDecimal bd = new BigDecimal(10000);
                    jackpotStr = (bigDecimal.multiply(bd)).intValue() + "";
                }
                lotteryDraw.setJackpot(jackpotStr);
                //获取开奖详细信息  6+1 与25选5 获取的方式不同
                List<LotteryDrawPrizeItem> itemList = new ArrayList<LotteryDrawPrizeItem>();
                try {
                    if (this.getLotteryType() == LotteryType.A_ZJ6) {
                        for (int j = 0; j < tableTag.getRows().length; j++) {
                            LotteryDrawPrizeItem item = new LotteryDrawPrizeItem();
                            TableColumn[] tableColumn = tableTag.getRow(j).getColumns();
                            //第五行 为开奖详细信息 第五行为特等奖  第六行为一等奖 第七行为二等奖 第八行为三等奖  第九行为四等奖 第十行为五等奖
                            if (j >= 4 && j <= 9) {
                                String prizeName = trimAll(tableColumn[0].getStringText());
                                String winningCount = tableColumn[2].getStringText();
                                String winningAmount = tableColumn[4].getStringText();
                                winningCount = trimAll(winningCount.replace("</b>", "").replace("<b>", ""));
                                winningAmount = trimAll(winningAmount.replace("</b>", "").replace("<b>", "").replace("元", ""));
                                item.setName(prizeName);
                                item.setWinningCount(winningCount);
                                item.setPrizeAmount(winningAmount);
                                itemList.add(item);
                            }
                        }
                        if (itemList == null) {
                            logger.error( this.getLotteryType() + "开奖详情的获取失败,未能获取开奖详细信息");
                            return null;
                        }
                    } else {
                        for (int j = 0; j < tableTag.getRows().length; j++) {
                            LotteryDrawPrizeItem item = new LotteryDrawPrizeItem();
                            TableColumn[] tableColumn = tableTag.getRow(j).getColumns();
                            if (j >= 4 && j <= 6) {
                                String prizeName = trimAll(tableColumn[0].getStringText());
                                String winningCount = tableColumn[1].getStringText();
                                String winningAmount = tableColumn[2].getStringText();
                                winningCount = trimAll(winningCount.replace("</b>", "").replace("<b>", ""));
                                winningAmount = trimAll(winningAmount.replace("</b>", "").replace("<b>", "").replace("元", ""));
                                item.setName(prizeName);
                                item.setWinningCount(winningCount);
                                item.setPrizeAmount(winningAmount);
                                itemList.add(item);
                            }
                        }
                        if (itemList == null) {
                            logger.error( this.getLotteryType() + "开奖详情的获取失败,未能获取开奖详细信息");
                            return null;
                        }
                    }
                    lotteryDraw.setResultDetail(itemList);
                } catch (Exception e) {
                    logger.error("开奖详情的获取出现异常");
                    logger.error(e.getMessage(), e);
                    return null;
                }
			}
			return lotteryDraw;
			
		} catch (Exception e) {
            logger.error("获取开奖详情失败！");
			logger.error(e.getMessage(), e);
			return null;
		}
	}

	@Override
	protected String getResultDetailUrl(String phase) {
		String resultUrl = getResultUrl(phase);
        logger.info(resultUrl);
		String encoding = CharsetConstant.CHARSET_GB2312;
		
		String data = null;
		try {
            synchronized (lock) {
                data = this.fetch(resultUrl, METHOD.GET, null, null, encoding);
            }
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		if (data == null || data.indexOf("404 Not Found") > 0) {
            logger.error("抓取超时，未抓取列表页面内容信息");
			return null;
		}
		
		Parser parser;
		try {
			parser = Parser.createParser(data, encoding);
		} catch (Exception e) {
			logger.error("解析html出错, data={}", data);
			return null;
		}
        NodeFilter tableFilter = new TagNameFilter("table");
        NodeFilter cssFilter = new HasAttributeFilter("class", "tb3");
        NodeFilter filter = new OrFilter(tableFilter, cssFilter);
		NodeList tableNodeList;
		try {
			tableNodeList = parser.extractAllNodesThatMatch(filter);
			if (tableNodeList != null && tableNodeList.size() > 0) {
					TableTag tableTag = (TableTag)tableNodeList.elementAt(0);
					String detailLink = "";
                    String aLink;
					for (int i = 0; i < tableTag.getRowCount(); i++) {
						TableRow everyRow = tableTag.getRow(i);
						for (int j = 0; j < everyRow.getColumnCount(); j++) {
							TableColumn firstColumn = everyRow.getColumns()[1];
							NodeList columnChildren = firstColumn.getChildren();
							for (int k = 0; k < columnChildren.size(); k++) {
								if (columnChildren.elementAt(k) instanceof LinkTag) {
									LinkTag linkTag = (LinkTag)columnChildren.elementAt(k);
                                    aLink = linkTag.getLink();
									String fetchPhase = linkTag.getLinkText();
									if (StringUtils.isNotEmpty(phase)) {
										if (fetchPhase.equals(phase)) {
											j = everyRow.getColumnCount();
											i = tableTag.getRowCount();
                                            String tempLink;
                                            if (StringUtils.isNotEmpty(aLink) && aLink.contains("openwin('/")) {
                                                tempLink = aLink.substring(aLink.indexOf("/") + 1);
                                                tempLink = tempLink.replace("'", "");
                                            } else {
                                                logger.error("未能获取得到开奖详情的url");
                                                return null;
                                            }
                                            if (StringUtils.isNotEmpty(tempLink)) {
                                                String[] aLinkArray = tempLink.split(",");
                                                if (aLinkArray != null && aLinkArray.length > 0) {
                                                    detailLink = aLinkArray[0];
                                                }
                                            } else {
                                                logger.error("未能获取得到开奖详情的url");
                                                return null;
                                            }
											break;
										}
									}
								}
							}
						}
					}
					return BASE_URL + detailLink;
			} else {
				logger.error("未找到class为tb3的table");
			}
		} catch (Exception e) {
            logger.error("获取开奖详情的url出现异常");
			logger.error(e.getMessage(), e);
		}
		return null;
	}

	@Override
	protected String getResultUrl(String phase) {
        return BASE_URL + "win/SResult.asp?flag=" + lotteryValueMap.get(this.getLotteryType()) + "&Sissue=" + phase + "&Eissue=" + phase;
	}

    /**
     * 从官网首页获取最新彩期
     * */
    private String getFetchPhaseFromIndexPage (String encoding, LotteryType lotteryType) {
        String toFetchPhase = "";
        String data = null;
        try {
            synchronized (lock) {
                data = this.fetch(BASE_URL_XML, METHOD.GET, null, null, encoding);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        if (data == null || data.indexOf("404 Not Found") > 0) {
            logger.error("抓取超时，未抓取列表页面内容信息");
            return null;
        }
        try {
            Document document = DocumentHelper.parseText(data);
            for (Object o : document.getRootElement().elements()) {
                Element element = (Element)o;
                Element lotterynameElement = element.element("lotteryname");
                Element issueElement = element.element("issue");
                String lotteryname = lotterynameElement.getText();
                if (StringUtils.isNotEmpty(lotteryname) && lotteryname.equals(lotteryNameMap.get(lotteryType))) {
                    toFetchPhase = issueElement.getText();
                    break;
                }
            }
            if (StringUtils.isEmpty(toFetchPhase)) {
                logger.warn("从官方首页未解析到要抓取的彩期号, lotteryType={}", lotteryType.getName() + "，将从开奖历史列表中获取！");
                return null;
            }
        } catch (Exception e) {
            logger.warn("从官方首页解析到要抓取的彩期号时发生异常，将从开奖历史列表中获取！");
            logger.error(e.getMessage(), e);
        }
        return toFetchPhase;
    }

    /**
     * 从开奖历史列表获取最新彩期
     * */
    private String getFetchPhaseFromHistoryPage (String encoding, LotteryType lotteryType) {
        String url = BASE_URL + "win/SResult.asp?flag=" + lotteryValueMap.get(this.getLotteryType());
        String toFetchPhase = "";
        String data = null;
        try {
            synchronized (lock) {
                data = this.fetch(url, METHOD.GET, null, null, encoding);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        if (data == null || data.indexOf("404 Not Found") > 0) {
            logger.error("抓取超时，未抓取列表页面内容信息");
            return null;
        }
        Parser parser;
        try {
            parser = Parser.createParser(data, encoding);
        } catch (Exception e) {
            logger.error("解析html出错, data={}", data);
            return null;
        }
        //满足两者之一的条件即可解析
        NodeFilter tableFilter = new TagNameFilter("table");
        NodeFilter cssFilter = new HasAttributeFilter("class", "tb3");
        NodeFilter filter = new OrFilter(tableFilter, cssFilter);
        NodeList tableNodeList;
        try {
            tableNodeList = parser.extractAllNodesThatMatch(filter);
            if (tableNodeList != null && tableNodeList.size() > 0) {
                TableTag tableTag = (TableTag)tableNodeList.elementAt(0);
                if (tableTag.getRowCount() > 1) {
                    TableRow everyRow = tableTag.getRow(1);
                    if (everyRow.getColumnCount() > 1) {
                        TableColumn firstColumn = everyRow.getColumns()[1];
                        toFetchPhase = firstColumn.toPlainTextString();
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        if (toFetchPhase == null) {
            logger.error("从官方开奖历史列表中未解析到要抓取的彩期号, lotteryType={}", lotteryType.getName());
            return null;
        }
        return  toFetchPhase;
    }

	
	private String getPhase (String phaseStr) {
		if (phaseStr == null || phaseStr.equals("")) {
			return null;
		}
		return trimAll(CoreStringUtils.substringBetween(phaseStr, "第", "期"));
	}
	
	private String getTimeDraw (String timeDrawStr) {
		if (timeDrawStr == null || timeDrawStr.equals("")) {
			return null;
		}
		
		String timeDraw = trimAll(timeDrawStr);
		timeDraw = timeDraw.replace("年", "-").replace("月", "-").replace("日", "");
		try {
			timeDraw = CoreDateUtils.formatDate(CoreDateUtils.parseDate(timeDraw));
			return timeDraw;
		} catch (Exception e) {
			logger.error("格式化开奖日期异常，{}", e);
			return null;
		}
	}
	
	private String getVolumeOfSales (String volumnOfSalesStr) {
		if (volumnOfSalesStr == null || volumnOfSalesStr.equals("")) {
			return null;
		}
		
		volumnOfSalesStr = volumnOfSalesStr.replace("元", "");
		
		Double voulmnOfSales = 0.0D;
		try {
			if (volumnOfSalesStr.endsWith("万")) {
				volumnOfSalesStr = volumnOfSalesStr.substring(0, volumnOfSalesStr.length() - 1);
				volumnOfSalesStr = trimAll(volumnOfSalesStr);
				voulmnOfSales = Double.parseDouble(volumnOfSalesStr) * 10000;
			} else {
				volumnOfSalesStr = trimAll(volumnOfSalesStr);
				voulmnOfSales = Double.parseDouble(volumnOfSalesStr);
			}
			NumberFormat nf = NumberFormat.getInstance();
			nf.setGroupingUsed(false);
			nf.setMaximumFractionDigits(0);
			nf.setMinimumFractionDigits(0);
			
			return nf.format(voulmnOfSales);
		} catch (Exception e) {
			logger.error("格式化销售额异常，{}" , e);
			return null;
		}
	}
	
	/**
	 * 替换字符串的各种特殊符号
	 * @param str
	 * @return
	 */
	public static String trimAll(String str){
		
		if(str != null && !str.equals("")){
	    	str = str.replaceAll(" ", "");
	    	str = str.replaceAll("　", "");
	    	str = str.replaceAll("&nbsp;", "");
	    	str = str.replaceAll("。", "");
	    	str = str.replaceAll("：", "");
	    	str = str.replaceAll(":", "");
	    	str = str.replaceAll(",", "");
            str = str.replaceAll("\t", "");
		}
    	return str;
	}

    @Override
    protected boolean usingProxy() {
        return true;
    }
}
